import os
import time
from datetime import datetime
import pytest
from selenium import webdriver
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from time import sleep

from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait


@pytest.fixture(scope="function")
def driver():
    service = Service(
        #提交最终代码脚本时，请将驱动路径换回官方路径"C:\\Users\\86153\\AppData\\Local\\Google\\Chrome\\Application\\chromedriver.exe"
        executable_path="D:\\Software\\Chrome\\Driver\\chromedriver.exe")
    driver = webdriver.Chrome(service=service)
    # driver.get("https://www.ctrip.com")
    driver.get("https://trains.ctrip.com/")
    driver.maximize_window()
    yield driver
    driver.quit()


class TestCtripFlight:
    #  test-code-start
    __ENTER_TO_TRAIN = (By.XPATH,"//button[@aria-label='火车票 按回车键打开菜单' and contains(@class, 'lsn_top_nav_qdgwe')]")
    __SIGNAL_TRAVEL = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[1]/div/ul/li[1]/i")
    __ROUND_TRAVEL = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[1]/div/ul/li[2]/i")
    __TRANSFER_TRAVEL = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[1]/div/ul/li[3]/i")
    __JUST_GAOTIE_SELECTOR = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[3]/i")
    __HAVE_SITE = (By.XPATH, "/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/div[1]/div[2]/div[1]/ul/li/i")
    __LEAVE_PLACE_INPUT = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[2]/div[3]/div/form[1]/input")
    __ARRIVE_PLACE_INPUT = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[2]/div[3]/div/form[2]/input")
    __FIRST_RESULT = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[2]/div[2]/div/div[2]/ul/li[1]")
    __LEAVE_DATA_INPUT = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[2]/div[4]/div[1]/div[1]")
    __ROUND_DATA_INPUT = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[2]/div[4]/div[1]/div[2]")
    __TARGET_DATA = (By.XPATH, "/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[2]/div[1]/div/div[2]/div[2]/ul[2]/li[17]")
    __QUERY_BUTTON = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div/div[2]/div[1]/div[2]/button")
    __RESULT_LIST = (By.XPATH,"//span[text()='登录可查看余票/票价']")
    __RESULT_HINT = (By.XPATH,"//h5[text()='未找到符合条件的车次']")
    __RESET_SITE = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/div[1]/div[2]/div[2]/div/span")
    __FIRST_SITE = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/div[1]/div[2]/div[2]/ul/li[1]/i")
    __LEAVE_DATA_FIRST = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/div[1]/div[2]/div[3]/ul/li[1]")
    __LEAVE_DATA_SECOND = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/div[1]/div[2]/div[3]/ul/li[2]")
    __ARRIVE_DATA_FIRST = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/div[1]/div[2]/div[4]/ul/li[1]")
    __ORDER_BY_PRICE = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div[3]/div[1]/div/ul[2]/li[3]")
    __OPEN_OR_ClOSE_SELECTOR = (By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/div[1]/div[3]")
    __FIRST_SELECTOR = [(By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/div[1]/div[2]/div[5]/ul/li[1]/i"),(By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/div[1]/div[2]/div[6]/ul/li[1]/i"),(By.XPATH,"/html/body/div[2]/div[2]/div/div/div/div[3]/div[2]/div[1]/div[2]/div[7]/ul/li[1]/i")]
    __RESET_SELECTOR = (By.XPATH,"//span[@class ='reset disabled']")

    # 行为
    # 最长的强制等待时间
    timeout = 10

    # 自定义封装查询组件函数，包含指定时间的显示等待
    def find_element(self,driver,by,value):
        try:
            WebDriverWait(driver, self.timeout).until(
                expected_conditions.visibility_of_element_located((by, value))
            )
            return WebDriverWait(driver, self.timeout).until(
                expected_conditions.element_to_be_clickable((by, value))
            )
        except Exception as e:
            print(f"元素定位失败！定位方式：{by}，元素路径：{value}，错误详情：{type(e).__name__}: {str(e)}")
            return None


    #  请在此处插入python+selenium代码
    @pytest.mark.parametrize(
        "leave_place,arrive_place",
        [
            ["北京","成都"],
            ["上海","广州"]
        ],ids=["CtripFlight_R001_001","CtripFlight_R001_002"]
    )
    def test_CtripFlight_R001(self,driver,request,leave_place,arrive_place):
        """
        从携程首页进入火车票购票页面，查询指定出发地与目的地
        :param driver: 驱动
        :param request: Pytest请求
        :param leave_place: 出发地
        :param arrive_place: 目的地
        :param round:返程查询  用于后续测试函数调用该参数
        :return:
        """
        status = True  # 用于保存脚本运行结果
        msg = None  # 用于接收运行过程中的错误信息
        actions = ActionChains(driver)
        try:
            # 进入购票页面
            # enter_ele = self.find_element(driver,*self.__ENTER_TO_TRAIN)
            # actions.move_to_element(enter_ele).click(enter_ele).perform()

            # 输入出发地和到达地
            leave_input = self.find_element(driver,*self.__LEAVE_PLACE_INPUT)
            leave_input.click()
            leave_input.clear()
            time.sleep(1)
            actions.click(leave_input).send_keys(leave_place).perform()
            time.sleep(1)
            self.find_element(driver,*self.__FIRST_RESULT).click()
            arrive_input = self.find_element(driver,*self.__ARRIVE_PLACE_INPUT)
            time.sleep(1)
            arrive_input.clear()
            time.sleep(1)
            actions.click(arrive_input).send_keys(arrive_place).perform()
            time.sleep(1)
            self.find_element(driver,*self.__FIRST_RESULT).click()

            # 输入出行日期
            self.find_element(driver,*self.__LEAVE_DATA_INPUT).click()
            self.find_element(driver, *self.__TARGET_DATA).click()

            # 执行搜索结果
            self.find_element(driver,*self.__QUERY_BUTTON).click()
            # 查找是否有结果渲染成功了
            # res是查询出来的火车票数据
            res = self.find_element(driver,*self.__RESULT_LIST)
            # 若没有相关数据 不一定是测试有问题 而可能是因为这个出发时间和地点本来就没票 因此还要检查是不是前端页面显示 未找到符合条件的车次
            if not res:
                hint_ele = self.find_element(driver,*self.__RESULT_HINT)
                if not res:
                    status = False
                    msg = "测试用例执行异常：不处于 无符合条件的火车票/有符合条件的火车票情况"
        except Exception as e:
            status = False   # 脚本运行异常
            msg = f"{type(e).__name__}:{str(e)}"
        assert status , f"查询失败，失败原因{msg}"
        # 通过 request.node.callspec.id 获取当前用例的 ids
        case_id = request.node.callspec.id
        self.take_screenshot(driver,case_id)


    @pytest.mark.parametrize(
        "leave_place",
        [
            "鲁尚武",
            "余范睿子"
        ],ids=["CtripFlight_R002_001","CtripFlight_R002_002"]
    )
    def test_CtripFlight_R002(self,driver,request,leave_place):
        """
        从携程首页进入火车票购票页面，查询指定出发地与目的地
        :param driver: 驱动
        :param request: Pytest请求
        :param leave_place: 出发地
        :param arrive_place: 目的地
        :return:
        """
        status = True  # 用于保存脚本运行结果
        msg = None  # 用于接收运行过程中的错误信息
        actions = ActionChains(driver)
        try:
            # 进入购票页面
            # enter_ele = self.find_element(driver,*self.__ENTER_TO_TRAIN)
            # actions.move_to_element(enter_ele).click(enter_ele).perform()

            # 输入出发地和到达地
            leave_input = self.find_element(driver,*self.__LEAVE_PLACE_INPUT)
            leave_input.click()
            leave_input.clear()
            time.sleep(1)
            actions.click(leave_input).send_keys(leave_place).perform()
            time.sleep(1)
            self.find_element(driver,*self.__FIRST_RESULT).click()

            # 检查是否有异常提示
            warn_hint = self.find_element(driver,*self.__FIRST_RESULT).text
            if "对不起，找不到：" not in warn_hint:
                status = False
                msg = f"网页提示信息不符合预取,实际提示信息为{warn_hint}"
        except Exception as e:
            status = False   # 脚本运行异常
            msg = f"{type(e).__name__}:{str(e)}"
        assert status , f"查询失败，失败原因{msg}"
        # 通过 request.node.callspec.id 获取当前用例的 ids
        case_id = request.node.callspec.id
        self.take_screenshot(driver,case_id)


    @pytest.mark.parametrize(
        "leave_place,arrive_place",
        [
            ["北京","成都"],
            ["上海","广州"]
        ],ids=["CtripFlight_R003_001","CtripFlight_R003_002"]
    )
    def test_CtripFlight_R003(self,driver,request,leave_place,arrive_place):
        """
        从携程首页进入火车票购票页面，查询指定出发地与目的地，
        并通过鼠标点击的方式依次对仅有票车次、车型选中第一个复选框，出发时间选中第二个复选框，并按照价格由高到低排序。
        :param driver: 驱动
        :param request: Pytest请求
        :param leave_place: 出发地
        :param arrive_place: 目的地
        :return:
        """
        global res
        status = True  # 用于保存脚本运行结果
        msg = None  # 用于接收运行过程中的错误信息
        actions = ActionChains(driver)
        try:
            # 进入购票页面
            # enter_ele = self.find_element(driver,*self.__ENTER_TO_TRAIN)
            # actions.move_to_element(enter_ele).click(enter_ele).perform()

            # 输入出发地和到达地
            leave_input = self.find_element(driver,*self.__LEAVE_PLACE_INPUT)
            leave_input.click()
            leave_input.clear()
            time.sleep(1)
            actions.click(leave_input).send_keys(leave_place).perform()
            time.sleep(1)
            self.find_element(driver,*self.__FIRST_RESULT).click()
            arrive_input = self.find_element(driver,*self.__ARRIVE_PLACE_INPUT)
            time.sleep(1)
            arrive_input.clear()
            time.sleep(1)
            actions.click(arrive_input).send_keys(arrive_place).perform()
            time.sleep(1)
            self.find_element(driver,*self.__FIRST_RESULT).click()

            # 输入出行日期
            self.find_element(driver,*self.__LEAVE_DATA_INPUT).click()
            self.find_element(driver, *self.__TARGET_DATA).click()

            # 执行搜索结果
            self.find_element(driver,*self.__QUERY_BUTTON).click()
            # 查找是否有结果渲染成功了
            # res是查询出来的火车票数据
            res = self.find_element(driver,*self.__RESULT_LIST)
            # 若没有相关数据 一定是测试有问题
            if res:
                # 点击仅有票
                self.find_element(driver, *self.__HAVE_SITE).click()
                # 点击第一个坐席选项
                self.find_element(driver,*self.__FIRST_SITE).click()
                # 点击第二个时间选项
                self.find_element(driver,*self.__LEAVE_DATA_SECOND).click()
                # 点击价格排序
                self.find_element(driver,*self.__ORDER_BY_PRICE).click()
                self.find_element(driver,*self.__ORDER_BY_PRICE).click()
            time.sleep(1)
        except Exception as e:
            status = False   # 脚本运行异常
            msg = f"{type(e).__name__}:{str(e)}"
        assert status and res, f"查询失败，失败原因{msg}"
        # 通过 request.node.callspec.id 获取当前用例的 ids
        case_id = request.node.callspec.id
        self.take_screenshot(driver,case_id)


    @pytest.mark.parametrize(
        "leave_place,arrive_place",
        [
            ["北京","成都"],
            ["上海","广州"]
        ],ids=["CtripFlight_R004_001","CtripFlight_R004_002"]
    )
    def test_CtripFlight_R004(self,driver,request,leave_place,arrive_place):
        """
        从携程首页进入火车票购票页面，查询指定出发地与目的地，
        通过鼠标点击的方式依次对仅有票车次、车型、出发时间、到达时间、坐席、出发车站、到达车站选中第一个可筛选项，并依次点击重置按钮取消选中，最后收起列表。
        :param driver: 驱动
        :param request: Pytest请求
        :param leave_place: 出发地
        :param arrive_place: 目的地
        :return:
        """
        global res
        status = True  # 用于保存脚本运行结果
        msg = None  # 用于接收运行过程中的错误信息
        actions = ActionChains(driver)
        try:
            # 进入购票页面
            # enter_ele = self.find_element(driver,*self.__ENTER_TO_TRAIN)
            # actions.move_to_element(enter_ele).click(enter_ele).perform()

            # 输入出发地和到达地
            leave_input = self.find_element(driver,*self.__LEAVE_PLACE_INPUT)
            leave_input.click()
            leave_input.clear()
            time.sleep(1)
            actions.click(leave_input).send_keys(leave_place).perform()
            time.sleep(1)
            self.find_element(driver,*self.__FIRST_RESULT).click()
            arrive_input = self.find_element(driver,*self.__ARRIVE_PLACE_INPUT)
            time.sleep(1)
            arrive_input.clear()
            time.sleep(1)
            actions.click(arrive_input).send_keys(arrive_place).perform()
            time.sleep(1)
            self.find_element(driver,*self.__FIRST_RESULT).click()

            # 输入出行日期
            self.find_element(driver,*self.__LEAVE_DATA_INPUT).click()
            self.find_element(driver, *self.__TARGET_DATA).click()

            # 执行搜索结果
            self.find_element(driver,*self.__QUERY_BUTTON).click()
            # 查找是否有结果渲染成功了
            # res是查询出来的火车票数据
            res = self.find_element(driver,*self.__RESULT_LIST)
            # 若没有相关数据 一定是测试有问题
            if res:
                # 点击仅有票
                self.find_element(driver, *self.__HAVE_SITE).click()
                # 点击第一个坐席选项
                self.find_element(driver,*self.__FIRST_SITE).click()
                # 点击第一个离开时间选项
                self.find_element(driver,*self.__LEAVE_DATA_FIRST).click()
                # 展开所有选项
                self.find_element(driver,*self.__OPEN_OR_ClOSE_SELECTOR).click()
                # 点击第一个到达时间选项
                self.find_element(driver,*self.__ARRIVE_DATA_FIRST).click()
                # 点击坐席、出发站、到达站的第一个选项
                self.find_element(driver,*self.__FIRST_SELECTOR[0]).click()
                self.find_element(driver,*self.__FIRST_SELECTOR[1]).click()
                self.find_element(driver,*self.__FIRST_SELECTOR[2]).click()
                # 执行重置操作
                reset_eles = driver.find_elements(*self.__RESET_SELECTOR)
                for ele in reset_eles:
                    ele.click()
                # 关上展开选项
                self.find_element(driver,*self.__OPEN_OR_ClOSE_SELECTOR).click()
            time.sleep(1)
        except Exception as e:
            status = False   # 脚本运行异常
            msg = f"{type(e).__name__}:{str(e)}"
        assert status and res, f"查询失败，失败原因{msg}"
        # 通过 request.node.callspec.id 获取当前用例的 ids
        case_id = request.node.callspec.id
        self.take_screenshot(driver,case_id)



    @pytest.mark.parametrize(
        "leave_place,arrive_place",
        [
            ["北京","成都"],
            ["上海","广州"]
        ],ids=["CtripFlight_R005_001","CtripFlight_R005_002"]
    )
    def test_CtripFlight_R005(self,driver,request,leave_place,arrive_place):
        """
        从携程首页进入火车票购票页面，查询指定出发地与目的地的往返车票
        :param driver: 驱动
        :param request: Pytest请求
        :param leave_place: 出发地
        :param arrive_place: 目的地
        :return:
        """
        status = True  # 用于保存脚本运行结果
        msg = None  # 用于接收运行过程中的错误信息
        actions = ActionChains(driver)
        try:
            # 进入购票页面
            # enter_ele = self.find_element(driver,*self.__ENTER_TO_TRAIN)
            # actions.move_to_element(enter_ele).click(enter_ele).perform()

            # 选择返程
            self.find_element(driver,*self.__ROUND_TRAVEL).click()

            # 输入出发地和到达地
            leave_input = self.find_element(driver,*self.__LEAVE_PLACE_INPUT)
            leave_input.click()
            leave_input.clear()
            time.sleep(1)
            actions.click(leave_input).send_keys(leave_place).perform()
            time.sleep(1)
            self.find_element(driver,*self.__FIRST_RESULT).click()
            arrive_input = self.find_element(driver,*self.__ARRIVE_PLACE_INPUT)
            time.sleep(1)
            arrive_input.clear()
            time.sleep(1)
            actions.click(arrive_input).send_keys(arrive_place).perform()
            time.sleep(1)
            self.find_element(driver,*self.__FIRST_RESULT).click()

            # 输入出行日期
            self.find_element(driver,*self.__LEAVE_DATA_INPUT).click()
            self.find_element(driver, *self.__TARGET_DATA).click()

            # 执行搜索结果
            self.find_element(driver,*self.__QUERY_BUTTON).click()
            # 查找是否有结果渲染成功了
            # res是查询出来的火车票数据
            res = self.find_element(driver,*self.__RESULT_LIST)
            # 若没有相关数据 不一定是测试有问题 而可能是因为这个出发时间和地点本来就没票 因此还要检查是不是前端页面显示 未找到符合条件的车次
            if not res:
                hint_ele = self.find_element(driver,*self.__RESULT_HINT)
                if not res:
                    status = False
                    msg = "测试用例执行异常：不处于 无符合条件的火车票/有符合条件的火车票情况"
        except Exception as e:
            status = False   # 脚本运行异常
            msg = f"{type(e).__name__}:{str(e)}"
        assert status , f"查询失败，失败原因{msg}"
        # 通过 request.node.callspec.id 获取当前用例的 ids
        case_id = request.node.callspec.id
        self.take_screenshot(driver,case_id)


    '''
    【说明】其他的测试用例只需要在上述的测试用例函数中添加少量个别点击操作即可完成，难度不大，为了节省时间此处不再撰写
    有疑问请评论区提出
    '''

    #  test-code-end

    @staticmethod
    def take_screenshot(driver, file_name):
        timestamp = datetime.now().strftime("%H%M%S%d%f")
        timestamped_file_name = f"{timestamp}_{file_name}.png"
        screenshots_dir = "screenshots"
        if not os.path.exists(screenshots_dir):
            os.makedirs(screenshots_dir)
        screenshot_file_path = os.path.join(screenshots_dir, timestamped_file_name)
        driver.save_screenshot(screenshot_file_path)

